(function($) {

	KOR.validator = function(selector, mixin) {
		return KOR.Validator.getInstanceOf(selector, mixin);
    };

	KOR.Validator = KOR.createClass(KOR.UI, {
        NAME: 'validator',

        errorClass: 'kor-error-message',
        hintClass: 'kor-field-hint-value',
        fieldErrorClass: 'kor-field-error',
        validationEvents: 'change',
		fieldEvents: 'blur focus',
        postFailureFieldEvents: 'keyup',
		postSubmitFieldEvents: 'keypress',
        errorMessagePosition: 'after',
		placeholderClassName: 'kor-field-error-message-placeholder',
		flashErrorTemplate: 'flashErrorTemplate',
		instantFlashErrors: false,

		registerEvents: function(){
			this._super();

			var obj = this;
			$(document).delegate(this.selector + ' [data-validate]', this.validationEvents, $.proxy(this, 'validatorsHandler'));
			$(document).delegate(this.selector + ' [data-validate]:not([data-field-validator-initialized])', 'mouseover focus', $.proxy(this, 'focusHandler'));
			$(document).delegate(this.selector + ' [data-validate]', 'blur', $.proxy(this, 'blurHandler'));
			$(document).delegate(this.selector + ' [data-validate]', this.NAME + ':failed', $.proxy(this, 'failureEventsHandler'));
			$(document).delegate(this.selector + ' [data-validate][data-validate-hint]', 'focus', $.proxy(this, 'clearHint'));
			$(document).delegate(this.selector + ' [data-validate][data-validate-hint]', 'blur', $.proxy(this, 'showHint'));
			$(document).delegate(this.selector + ':not([data-validator-initialized])', 'focus mouseover', $.proxy(this, 'formHandler'));
			if (this.instantFlashErrors) {
				this.observe(this.NAME+':RenderInstantFlashErrors', $.proxy(this, 'renderFlashErrors'))
			}
		},

		renderFlashErrors: function(ev){
			var obj = this;
			obj.flashErrors = []
			if($('.'+this.fieldErrorClass).length){
				if(this.form.attr('data-flash-error-keys')){
					var args = this.form.attr('data-flash-error-keys').split(' ')
						args.push(function(error){
							obj.flashErrors.push(error)
						})
						KOR.ObjectFactory.getExtStringBabushka.apply(this, args)
				}else{
					$.each($('.'+this.fieldErrorClass+'[data-flash-error-keys]', this.form), function(i,v){
						var args = $(v).attr('data-flash-error-keys').split(' ')
						args.push(function(error){
							obj.flashErrors.push(error)
						})
						KOR.ObjectFactory.getExtStringBabushka.apply(this, args)
					})
				}
			}
			if(obj.flashErrors.length){
				obj.flashErrorSelector.uTemplate(obj.flashErrorTemplate, {errorList:obj.flashErrors})
			}else{
				$(obj.flashErrorSelector).html('')
			}
		},

		focusHandler: function(ev){
			this.setValidator($(ev.target));
		},

        blurHandler: function(ev) {
			//pass
        },

        clearHint: function(ev){
			var field = $(ev.target);
            var hint = field.attr('data-validate-hint')
            if(field.val() == hint){
                field.val('')
            }
            field.removeClass(this.hintClass)
        },

		showHint: function(ev){
			var field = $(ev.target);
            var hint = field.attr('data-validate-hint')
            if(field.val() == ''){
                field.val(hint).addClass(this.hintClass)
            }
		},

		getPlaceholder: function(el){
			var field = el || this.field
			return (field.data('error-placeholder')) ? field.data('error-placeholder') : this.setPlaceholder()
		},

		setPlaceholder: function() {
			var position = this.field.attr('data-error-position') || this.form.attr('data-error-position') || this.errorMessagePosition
			var pName = this.field.attr('data-error-placeholder-name') || this.field.attr('name')+'Error'
			if(position == 'placeholder'){
				this.field.data('error-placeholder', $('[data-name="'+pName+'"]', this.field.closest('form')))
			}else if (position == 'before' || position == 'after'){
				position = this.capitalizeString(position)
				this.field.data('error-placeholder', $('<div class="'+this.placeholderClassName+'" data-name="'+pName+'"></div>')['insert'+position](this.field))
			}else{
				var pos = position.split(' ')
				if(pos[pos.length-1] == 'after' || pos[pos.length-1] == 'before'){
					pos = this.capitalizeString(pos[pos.length-1])
					position = position.replace('after', '').replace('before', '')
				}else{
					pos = this.capitalizeString(this.errorMessagePosition)
				}
				this.field.data('error-placeholder', $('<div class="'+this.placeholderClassName+'" data-name="'+pName+'"></div>')['insert'+pos](this.field.closest(position)))

			}
			return this.field.data('error-placeholder')
		},

		setValidator: function(field){
			this.field = field;
			this.form = field.closest('form');
			field.attr('data-field-validator-initialized', 'true').data('validator-instance', $.extend({},this))
		},

		getValidator: function(field) {
			var target = $(field)
			if(!target.data('validator-instance')){
				this.setValidator(target)
			}
			return target.data('validator-instance')
		},

		formHandler: function(ev) {
			var form = $(ev.currentTarget)
			if(!form.data('validator-instance')){
				this.isFormValid(form, true)
				this.flashErrorSelector = $(form.attr('data-flash-error-selector'))
				if(this.flashErrorSelector.length){
					this.observe(this.NAME+':RenderFlashErrors', $.proxy(this, 'renderFlashErrors'))
				}
				form.attr('data-validator-initialized', 'true').data('validator-instance', true)
				form.bind('submit', $.proxy(this, 'submitHandler'))
			}
		},

		validatorsHandler: function(ev){
			var obj = this.getValidator(ev.target);

			if(!obj.field.data('kor-init-validators')){
				var events = obj.field.attr('data-validate-events') || obj.form.attr('data-validate-events') || this.fieldEvents;
                obj.field.bind(events, $.proxy(obj, 'validateHandler'));
				obj.field.data('kor-init-validators', true);
			}
		},

        failureEventsHandler: function(ev){
			var obj = this.getValidator(ev.target);

            if(obj.field.data('kor-failed-validation') && !obj.field.data('kor-post-failure-validators')){
                var postFailureEvents = obj.field.attr('data-validate-post-failure-events') || obj.form.attr('data-validate-post-failure-events') || this.postFailureFieldEvents;
                obj.field.bind(postFailureEvents, $.proxy(obj, 'validateHandler'));
				obj.field.data('kor-post-failure-validators', true);
            }

			if(obj.field.data('kor-submitted') && !obj.field.data('kor-post-submit-validators')){
				var postSubmitEvents = obj.field.attr('data-validate-post-submit-events') || obj.form.attr('data-validate-post-submit-events') || this.postSubmitFieldEvents;
				obj.field.bind(postSubmitEvents, $.proxy(obj, 'validateHandler'));
				obj.field.data('kor-post-submit-validators', true);
			}
        },

		validateHandler: function(ev){
			this.validate()
			this.fire(this.NAME+':RenderInstantFlashErrors')
		},

		validate: function(field, silent){
            var obj = field ? this.getValidator(field) : this;

			var rules = obj.field.attr('data-validate').split(' ');
            var validation;
			var pass = true;

			$.each(rules, function(i,rule){
				if(pass){
					var args = rule.match(/\(.*?\)/);
					if(args){
						args = args[0];
						rule = rule.replace(args,'');
                        args = eval('obj.getArguments'+ args)
					}
                    if(KOR.Validator.rules[rule]) {
						obj.ruleName = rule;
                        validation = $.extend({}, obj, KOR.Validator.rules[rule]);
						pass = validation.isValid(obj.getFieldValue(), args);
					}
				}
			});
			if(pass){
				validation.renderSuccess(obj)
                obj.field.removeData('kor-failed-validation')
                obj.field.trigger(obj.NAME+':success')
				return true;
			} else if (pass == null) {
                validation.skip(obj)
                return true;
            }else{
                obj.field.data('kor-failed-validation', true)
				if(!silent){
					if(obj.field.attr('data-validate-supress-errors') != "true"){
						validation.renderFailure(obj)
					}
					obj.field.trigger(obj.NAME+':failed')
				}

				return false;
			}
		},

		isFormValid: function(form, silent){
			var obj = this
			var fields = form.find('[data-validate]');
			var valid = true;
			fields.each(function(){
				var field = $(this);
				if (!silent && !field.data('kor-submitted')) { field.data('kor-submitted', true) }
				if(!obj.validate(field, silent)){
					valid = false;
				}
			});
			if(!silent){
				this.fire(this.NAME+':RenderFlashErrors')
			}
			valid ? form.attr('data-valid', true).removeAttr('data-invalid') : form.attr('data-invalid', true).removeAttr('data-valid')
			return valid ? obj.formValid(form, silent) : valid;
		},

		submitHandler: function(ev, silent){
			var obj = this;
			var form = $(ev.target).closest(this.selector);
			if($('input[type=hidden][name=cancel]', form).length > 0){
				form.trigger('cancel')
				return true;
			}
			var valid = obj.isFormValid(form, silent);
			if(obj.ajaxSubmit && valid){
				form.removeAttr('data-invalid')
                obj.ajaxSubmitHandler(ev)
                return false
            }

			return valid;
		},

        isAjaxSuccess: function(data){
			data = KOR.decodeJSON(data);
			if(data.error)
				return false;
			else{
				return true;
			}
		},

		ajaxSuccess: function(data, form){
			form.removeClass(this.failureClass).addClass(this.successClass);
			form.trigger('validator:ajax-success');
		},

		ajaxFailure: function(data, form){
			form.removeClass(this.successClass).addClass(this.failureClass);
			form.trigger('validator:ajax-failure');
		},

        ajaxSubmitHandler: function(ev) {
            var form = $(ev.target)
            var obj = this
            $.ajax({
                type: 'POST',
                url: form.attr('action'),
                data: form.serialize(),
                success: function(data){
                    obj.isAjaxSuccess(data) == true ? form.attr('ajaxSuccess') || obj.ajaxSuccess(data, form) : form.attr('ajaxFailure') || obj.ajaxFailure(data, form)
                },
                error: function(){
                    form.attr('ajaxFailure') || obj.ajaxFailure()
                }
            });
        },

		formValid: function(ev){
			return true;
		},

		failure: function(ev){
			return false;
		},

        getFieldValue: function(field){
            var f = field ? $(field) : this.field;
            if(f.attr('type') == 'checkbox'){
                return f.attr('checked')
            }else{
                return (f.val() == f.attr('data-validate-hint')) ? '' : $.trim(f.val())
            }
        },

        getArguments: function(){
            return Array.prototype.slice.call(arguments, 0);
        }
	});

    KOR.Validator.Validations ={}

    KOR.Validator.Validations.AbstractValidation = {
        errorTemplate: 'validationErrorTemplate',

        init: function(args){
        	this.args = args;
        },
        
        isValid: function(value, args){
            this.init(args);
            return this.validate(value);
        },

        validate: function(value) {
            return true;
        },

        renderSuccess: function(){
            this.clearFieldMessages()
			return this;
        },

        renderFailure: function(){
			this.failureMessage();
			return this;
        },

		failureMessage: function(){
			this.renderFailureMessage('fieldFailureMessageInvalid');
		},

		capitalizeString: function(string){
			return string.charAt(0).toUpperCase() + string.slice(1)
		},

		renderFailureMessage: function(extStr, args){
            var obj = this;
            var m = this.field.attr('data-validate-error-message-'+ obj.ruleName) || this.field.attr('data-validate-error-message')
			if(m){
				obj.renderFailureMessageTemplate(m);
			}else{
				KOR.ObjectFactory.getExtString(extStr, args, function(message){
					obj.renderFailureMessageTemplate(message);
				});
			}
		},

		renderFailureMessageTemplate: function(message){
            this.clearFieldMessages()
            this.field.addClass(this.fieldErrorClass);
			this.getPlaceholder().uTemplate(this.errorTemplate,{
				errorClass: this.errorClass,
				errorMessage: message
			})
		},

		successMessage: function(){
			return this;
		},

		renderSuccessMessage: function(){
			return this;
		},

		renderSuccessMessageTemplate: function(){
            return this;
        },

        clearFieldMessages: function(){
            this.field.removeClass(this.fieldErrorClass);
			this.getPlaceholder().html('')
        },

        skip: function(){
            this.clearFieldMessages()
            return this;
        }
    }

    KOR.Validator.Validations.BasicValidation = $.extend({}, KOR.Validator.Validations.AbstractValidation, {
		validate: function(value){
            if (!value){ return null }
            return $.trim(value) ? true : false
		}
	})

	KOR.Validator.Validations.RegexValidation = $.extend({}, KOR.Validator.Validations.AbstractValidation, {
		pattern : null,

		validate: function(value){
            if (!value){ return null }
			return $.trim(value).match(this.pattern) ? true : false
		}
	});

	KOR.Validator.Validations.AjaxValidation = $.extend({}, KOR.Validator.Validations.AbstractValidation, {
		service : null,

		validate: function(value){
			var obj = this;
			$.ajax({
				type: 'POST',
				url: obj.service,
				data: value,
				success: function(data){
					obj.isAjaxSuccess(data) ? obj.renderSuccess() : obj.renderFailure()
				},
				error: obj.renderFailure
			});
			return null
		},

		isAjaxSuccess: function(data){
			data = KOR.decodeJSON(data);
			return data.valid;
		}
	});

    $.extend(KOR.namespace('KOR.Validator.rules'), {
        'dependsOn': $.extend({}, KOR.Validator.Validations.BasicValidation,{
            validate: function(value){
                if(this.args){
                    var masterValue = this.getFieldValue(this.args[0])
					if(this.args.length == 1 && masterValue){
						return true;
					} else if(this.args.length == 2 && masterValue == this.args[1]) {
						return true;
					}
				}
                return null;
            }
        }),

		'requiredRadioGrp' : $.extend({}, KOR.Validator.Validations.BasicValidation,{
			validate : function(value){
				value = (!$('[name="'+this.field.attr('name')+'"]:checked').length) ? false : true
				return value != '';
			},
			failureMessage: function(){
				this.renderFailureMessage('fieldFailureMessageRequired');
			},

			renderSuccess: function(){
				this.clearFieldMessages($('[name="'+this.field.attr('name')+'"]').first())
				return this;
			},

			renderFailureMessageTemplate: function(message){
				var field = $('[name="'+this.field.attr('name')+'"]').first()
				this.clearFieldMessages(field)
				field.addClass(this.fieldErrorClass);
				field.uTemplate(this.errorTemplate, {
					errorClass: this.errorClass,
					errorMessage: message
				}, this.errorMessagePosition);
			},

			clearFieldMessages: function(field){
				field.removeClass(this.fieldErrorClass);

				if (this.errorMessagePosition == 'after') {
					field.next('.'+this.errorClass).remove()
				}else{
					field.prev('.'+this.errorClass).remove();
				}
			}
		}),

		'required' : $.extend({}, KOR.Validator.Validations.BasicValidation,{
			validate : function(value){
				return value != '';
			},
			failureMessage: function(){
				this.renderFailureMessage('fieldFailureMessageRequired');
			}
		}),

		'letters' : $.extend({}, KOR.Validator.Validations.BasicValidation,{
			validate: function(value){
                if (!value){ return null }
				var m = value.match(/^[A-Za-z]+$/);
				if(m){
					if(this.args && this.args.length == 1 && this.args[0] == m[0].length){
						return true;
					} else if(this.args && this.args.length == 2 && this.args[0] == null && this.args[1] && m[0].length <= this.args[1]) {
						return true;
					} else if(this.args && this.args.length == 2 && this.args[1] == null && this.args[0] && m[0].length >= this.args[0]) {
						return true;
					} else if(this.args && this.args.length == 2 && this.args[1] && this.args[0] && m[0].length >= this.args[0] && m[0].length <= this.args[1]){
						return true;
					} else if(!this.args){
						return true;
					}
					return false;
				}
				return false;
			},
			failureMessage: function(){
				if(this.args && this.args.length == 1){
					this.renderFailureMessage('fieldFailureMessageLettersExact', [this.args[0]]);
				} else if (this.args && this.args.length == 2 && this.args[0] == null && this.args[1]) {
					this.renderFailureMessage('fieldFailureMessageLettersMax', [this.args[1]]);
				} else if (this.args && this.args.length == 2 && this.args[1] == null && this.args[0]) {
					this.renderFailureMessage('fieldFailureMessageLettersMin', [this.args[0]]);
				} else if (this.args && this.args.length == 2 && this.args[1] && this.args[0]) {
					this.renderFailureMessage('fieldFailureMessageLettersRange', [this.args[0], this.args[1]]);
				} else {
					this.renderFailureMessage('fieldFailureMessageLetters');
				}
			}
		}),

		'qty' : $.extend({}, KOR.Validator.Validations.BasicValidation,{
			validate: function(value){
                if (!value){ return null }
				var m = value.match(/^[0-9]+$/);
				if(m){
					if(this.args && this.args.length == 1 && this.args[0] == m[0]){
						return true;
					} else if(this.args && this.args.length == 2 && this.args[0] == null && this.args[1] && m[0] <= this.args[1]) {
						return true;
					} else if(this.args && this.args.length == 2 && this.args[1] == null && this.args[0] && m[0] >= this.args[0]) {
						return true;
					} else if(this.args && this.args.length == 2 && this.args[1] && this.args[0] && m[0] >= this.args[0] && m[0] <= this.args[1]){
						return true;
					} else if(!this.args){
						return true;
					}
					return false;
				}
				return false;
			},
			failureMessage: function(){
				if (this.args && this.args.length == 1){
					this.renderFailureMessage('fieldFailureMessageQuantityExact', [this.args[0]]);
				} else if (this.args && this.args.length == 2 && this.args[0] == null && this.args[1]) {
					this.renderFailureMessage('fieldFailureMessageQuantityMax', [this.args[1]]);
				} else if (this.args && this.args.length == 2 && this.args[1] == null && this.args[0]) {
					this.renderFailureMessage('fieldFailureMessageQuantityMin', [this.args[0]]);
				} else if (this.args && this.args.length == 2 && this.args[1] && this.args[0]) {
					this.renderFailureMessage('fieldFailureMessageQuantityRange', [this.args[0], this.args[1]]);
				} else {
					this.renderFailureMessage('fieldFailureMessageQuantity');
				}
			}
		}),

		'numbers' : $.extend({}, KOR.Validator.Validations.BasicValidation,{
			validate: function(value){
                if (!value){ return null }
				var m = value.match(/^[0-9]+$/);
				if(m){
					if(this.args && this.args.length == 1 && this.args[0] == m[0].length){
						return true;
					} else if(this.args && this.args.length == 2 && this.args[0] == null && this.args[1] && m[0].length <= this.args[1]) {
						return true;
					} else if(this.args && this.args.length == 2 && this.args[1] == null && this.args[0] && m[0].length >= this.args[0]) {
						return true;
					} else if(this.args && this.args.length == 2 && this.args[1] && this.args[0] && m[0].length >= this.args[0] && m[0].length <= this.args[1]){
						return true;
					} else if(!this.args){
						return true;
					}
					return false;
				}
				return false;
			},
			failureMessage: function(){
				if(this.args && this.args.length == 1){
					this.renderFailureMessage('fieldFailureMessageNumbersExact', [this.args[0]]);
				} else if (this.args && this.args.length == 2 && this.args[0] == null && this.args[1]) {
					this.renderFailureMessage('fieldFailureMessageNumbersMax', [this.args[1]]);
				} else if (this.args && this.args.length == 2 && this.args[1] == null && this.args[0]) {
					this.renderFailureMessage('fieldFailureMessageNumbersMin', [this.args[0]]);
				} else if (this.args && this.args.length == 2 && this.args[1] && this.args[0]) {
					this.renderFailureMessage('fieldFailureMessageNumbersRange', [this.args[0], this.args[1]]);
				} else {
					this.renderFailureMessage('fieldFailureMessageNumbers');
				}
			}
		}),

		'range' : $.extend({}, KOR.Validator.Validations.BasicValidation,{
			validate: function(value){
                if (!value){ return null }
				var m = value.match(/^[\S]+$/);
				if(m){
					if(this.args && this.args.length == 1 && this.args[0] == m[0].length){
						return true;
					} else if(this.args && this.args.length == 2 && this.args[0] == null && this.args[1] && m[0].length <= this.args[1]) {
						return true;
					} else if(this.args && this.args.length == 2 && this.args[1] == null && this.args[0] && m[0].length >= this.args[0]) {
						return true;
					} else if(this.args && this.args.length == 2 && this.args[1] && this.args[0] && m[0].length >= this.args[0] && m[0].length <= this.args[1]){
						return true;
					} else if(!this.args){
						return true;
					}
					return false;
				}
				return false;
			},
			failureMessage: function(){
				if(this.args && this.args.length == 1){
					this.renderFailureMessage('fieldFailureMessageCharactersExact', [this.args[0]]);
				} else if (this.args && this.args.length == 2 && this.args[0] == null && this.args[1]) {
					this.renderFailureMessage('fieldFailureMessageCharactersMax', [this.args[1]]);
				} else if (this.args && this.args.length == 2 && this.args[1] == null && this.args[0]) {
					this.renderFailureMessage('fieldFailureMessageCharactersMin', [this.args[0]]);
				} else if (this.args && this.args.length == 2 && this.args[1] && this.args[0]) {
					this.renderFailureMessage('fieldFailureMessageCharactersRange', [this.args[0], this.args[1]]);
				} else {
					this.renderFailureMessage('fieldFailureMessageCharacters');
				}
			}
		}),
		/*
		'matchfield' : $.extend({}, KOR.Validator.Validations.BasicValidation,{
			validate: function(fieldValue){
				var val = $(this.args[0]).val()
				var el = $(this.args[0])
				var field = this.field
				if(this.args[1] == 'master'){
					if(el.data('kor-init-validators')){
						$(el).data('validator-instance').validate()
					}
					return true
				}

				return (val!='') ? fieldValue == val : false;
			},
			failureMessage: function(){
				this.renderFailureMessage('fieldFailureMessageMatchValue');
			}
		}),
		*/

		'matchfield' : $.extend({}, KOR.Validator.Validations.BasicValidation,{
			validate: function(fieldValue){
				var val = $(this.args[0]).val()
				var el = $(this.args[0])
				var field = this.field
				if(this.args[1] == 'master'){
					if(el.data('kor-init-validators')){
						$(el).data('validator-instance').validate()
					}else{
						return true
					}
				}
				return (val!='') ? fieldValue == val : false;
			},
			failureMessage: function(){
				this.renderFailureMessage('fieldFailureMessageMatchValue');
			},

            renderFailureMessageTemplate: function(message){
                var field = $(this.args[0])
                this.clearFieldMessages()
                this.field.addClass(this.fieldErrorClass);
                this.getPlaceholder().uTemplate(this.errorTemplate, {
                    errorClass: this.errorClass,
                    errorMessage: message
                });

                field.addClass(this.fieldErrorClass);
                field.data('validator-instance').getPlaceholder().uTemplate(this.errorTemplate, {
                    errorClass: this.errorClass,
                    errorMessage: message
                });
            },

			clearFieldMessages: function(){
				var field = $(this.args[0])
				this.field.removeClass(this.fieldErrorClass);
				this.getPlaceholder().html('')
				field.removeClass(this.fieldErrorClass);
				if(field.data('validator-instance')){
					field.data('validator-instance').getPlaceholder().html('')
				}
			}
		}),
		
		'joined' : $.extend({}, KOR.Validator.Validations.BasicValidation,{
			validate: function(fieldValue){
				if($(this.args[0]).hasClass(this.fieldErrorClass)){
					return false
				}
				return true
			},
			renderFailureMessageTemplate: function(message) {
				this.field.removeClass(this.fieldErrorClass)
			}
		}),
		
		'creditcard' : $.extend({}, KOR.Validator.Validations.BasicValidation,{
			validate: function(value){
                if (!value){ return null }
				value = value.replace(/\D/g, '')
				//test cc number based on Luhn algorithm
				cc_array = value.split( "" )
				cc_array.reverse()
				digit_string = ""
				
				for ( counter=0; counter < cc_array.length; counter++ )
				{
					current_digit = parseInt( cc_array[counter] )
					
					if (counter %2 != 0)
					{
						cc_array[counter] *= 2
					}
					
					digit_string += cc_array[counter]
				
				}
				
				digit_sum = 0
				
				for ( counter=0; counter<digit_string.length; counter++ )
				{
					current_digit = parseInt( digit_string.charAt(counter) )
					digit_sum += current_digit
				}
				
				if ( digit_sum % 10 == 0 )
				{
					return true
				}
				else
				{
					return false
				}
			},
			failureMessage: function(){
				this.renderFailureMessage('fieldFailureMessageCreditCard');
			}
		}),

		'price' : $.extend({}, KOR.Validator.Validations.RegexValidation,{
			pattern: /^\D*[0-9]{1,3}(?:[\.,]?[0-9]{3})*(?:[\.,](?:[0-9]{1,2})?)?\D*$/,
			failureMessage: function(){
				this.renderFailureMessage('fieldFailureMessagePrice');
			}
		}),

		'email' : $.extend({}, KOR.Validator.Validations.RegexValidation,{
			pattern: /^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/,
			failureMessage: function(){
				this.renderFailureMessage('fieldFailureMessageEmail');
			}
		}),

		'zipcode' : $.extend({}, KOR.Validator.Validations.RegexValidation,{
			pattern : /^\d{5}([\-]\d{4})?$/,
			
			// zip code patterns by country code
			patterns : {
				"AT": /^[0-9]{4}$/,
				"CA": /^[A-CEGHJ-PR-TV-Z][0-9][A-CEGHJ-PR-TV-Z] [0-9][A-CEGHJ-PR-TV-Z][0-9]$/,
				"CH": /^[0-9]{4}$/,
				"DE": /^[0-9]{5}$/,
				"GB": /^(GIR 0AA)|(((A[BL]|B[ABDHLNRSTX]?|C[ABFHMORTVW]|D[ADEGHLNTY]|E[HNX]?|F[KY]|G[LUY]?|H[ADGPRSUX]|I[GMPV]|JE|K[ATWY]|L[ADELNSU]?|M[EKL]?|N[EGNPRW]?|O[LX]|P[AEHLOR]|R[GHM]|S[AEGKLMNOPRSTY]?|T[ADFNQRSW]|UB|W[ADFNRSV]|YO|ZE)[1-9]?[0-9]|((E|N|NW|SE|SW|W)1|EC[1-4]|WC[12])[A-HJKMNPR-Y]|(SW|W)([2-9]|[1-9][0-9])|EC[1-9][0-9]) [0-9][ABD-HJLNP-UW-Z]{2})$/
			},
			
			init: function(args){
				this.args = args;
                if(this.args && this.args.length >= 1) {
                	var countryCode = this.args[0];
                	if (this.patterns[countryCode]) {
                		this.pattern = this.patterns[countryCode];
                	}
				}
			},

			failureMessage: function(){
				if(this.args && this.args.length >= 1 && this.patterns[this.args[0]]) {
                	this.renderFailureMessage('fieldFailureMessageZipcode.' + this.args[0]);
				} else {
					this.renderFailureMessage('fieldFailureMessageZipcode');
				}
			}
		}),

		'words' : $.extend({}, KOR.Validator.Validations.RegexValidation,{
			pattern: /^\w+\s+\w+[\s\w]+$/,
			failureMessage: function(){
				this.renderFailureMessage('fieldFailureMessageWords');
			}
		})
	});
})(jQuery);
